/*
 *  Copyright 2008 Mark Ashworth <javameme@gmail.com>.
 * 
 *  Licensed under the GNU General Public License v3 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.gnu.org/licenses/gpl-3.0.html
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package xperiment.metaphor.model;

import java.lang.reflect.Method;
import xperiment.metaphor.model.reflection.MethodSignature;
import metaphor.persistence.UnsavedValueStrategy;
import metaphor.persistence.UnsavedValueStrategyException;

/**
 * This class determines whether the object should be considered new by
 * checking if the object is an instance of Persistent and the id value is
 * not null. If the object is not a Persistent object then the object is tested
 * for the existence of the getId() method and if it exists it's value is tested
 * against null.
 * 
 * @author Mark Ashworth <javameme@gmail.com>
 * @version 1.0.0
 */
public class ReflectionUnsavedValueStrategy implements UnsavedValueStrategy {
    
    /* The method signature of the method that should be used for the test */
    private MethodSignature method = new MethodSignature("getId", new String[]{});
    
    /* The parameter values that should be used for the method */
    private Object[] values = new Object[0];
    
    /**
     * Default constructor but the method signature must be set
     */
    public ReflectionUnsavedValueStrategy() {
        super();
    }
    
    /**
     * Constructor for specifying the method which has no parameter values
     * @param method The method signature
     */
    public ReflectionUnsavedValueStrategy(MethodSignature method) {
        super();
        this.method = method;
    }
    
    /**
     * Constructor for specifying the method with the parameter values 
     * that should be used.
     * @param method The method signature
     * @param values The parameter values
     */
    public ReflectionUnsavedValueStrategy(MethodSignature method, Object[] values) {
        super();
        this.method = method;
        this.values = values;
    }

    /**
     * Returns the method that the reflection strategy will use to determine
     * if an object is new or not
     * @return MethodSignature
     */
    public MethodSignature getMethod() {
        return method;
    }

    /**
     * Returns the method parameter values that the reflection strategy will use 
     * to determine if an object is new or not
     * @return Object[]
     */
    public Object[] getValues() {
        return values;
    }
    
    /**
     * Returns whether the object should be considered a new object and the
     * persistent mechanism should try to insert the object. If false is 
     * returned then the persistence mechanism can try to update the
     * object in the persistent store.
     * 
     * @param object The object that should be tested
     * @return true if object is new else false
     * @throws UnsavedValueStrategyException If the method failed
     */
    public boolean isNew(Object object) throws UnsavedValueStrategyException {
        
        if (object == null) {
            throw new UnsavedValueStrategyException("The object is null");
        }
        if (getMethod() == null) {
            throw new UnsavedValueStrategyException("The method signature is null");
        }
        if (getMethod().getName() == null || getMethod().getParameters() == null) {
            throw new UnsavedValueStrategyException("The method signature is incomplete");
        }
        Object[] v = getValues();
        if (v == null) {
            v = new Object[0];
        }
        
        try {
            Class clazz = object.getClass();
            Method m = clazz.getMethod(getMethod().getName(), getMethod().getParameterTypes());
            Object result = m.invoke(object, v);
            return result == null;
        } catch (Exception e) {
            throw new UnsavedValueStrategyException("Unable to determine if object " + object + " is new", e);
        }
    }

    /**
     * Returns the String representation of the object
     * @return String
     */
    @Override
    public String toString() {
        return "ReflectionUnsavedValueStrategy (" + (getMethod() == null ? "null" : getMethod()) + ")";
    }
}
